<?php

class HasAndBelongsToManyAssociationDAO extends AssociationDAO
{

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/domain/association/AssociationDAO#afterSave($client)
   *
   * TODO: check if associations are unique
   * TODO: check how to add extra data to association table, such as order information
   */
  public function afterSave(&$client, $params = array())
  {
    $def = $this->getDefinition();

    $name = $def->getName();

    if (isset($client[$name])) {
      $pk = $def->getPrimaryKey();

      if (! isset($client[$pk])) {
        throw new Exception('Cannot save associated data: primary key of source object not set');
      }

      $fk = $def->getForeignKey();

      $_client = array(
        $pk => $client[$pk]
      );

      $this->find($_client);

      $diff = sy_data_diff($_client[$name], $client[$name], $def->getTarget()->getPkName());

      $rep = Repository::getInstance($def->getTarget()->getName());

      if ($def->isExclusive()) {
        foreach ($diff['delete'] as $row) {
          $rep->delete($row);
        }
      }

      $client[$name] = $diff['keep'];

      foreach ($client[$name] as &$data) {
        $rep->save($data);
      }

      $this->saveHABTM($client);
    }
  }

  protected function saveHABTM($client)
  {
    $def = $this->getDefinition();

    $name = $def->getName();

    $atable = $def->getAssociationTable();
    $afk = $def->getAssociationForeignKey();
    $apk = $def->getAssociationPrimaryKey();

    $fk = $def->getForeignKey();
    $pk = $def->getPrimaryKey();

    $id = $client[$pk];

    $dao = DAO::getInstance();

    $dao->query("DELETE FROM $atable WHERE $apk = ?")->execute($id);

    $data = array($apk => null, $afk => null);

    $query = $dao->query("INSERT INTO $atable " . QueryObject::buildInsert($data));

    foreach ($client[$name] as $row) {
      $data[$apk] = $id;
      $data[$afk] = $row[$fk];

      $result = $query->execute($data);
    }
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/domain/association/AssociationDAO#find($clients)
   */
  public function find(&$clients, $params = null)
  {
    $def = $this->getDefinition();

    $dao = DAO::getInstance();

    $name = $def->getName();

    $atable = $def->getAssociationTable();
    $afk = $def->getAssociationForeignKey();
    $apk = $def->getAssociationPrimaryKey();

    $table = $def->getTarget()->getTable();
    $fk = $def->getForeignKey();
    $pk = $def->getPrimaryKey();

    $fields = Repository::collectFields($def->getTarget());

    $query = $dao->query()->from($table)->join("$atable ON $atable.$afk = $table.$fk", QueryObject::JOIN_LEFT);

    $params = (array) $params;
    if (isset($params['where'])) {
      $query->where($params['where']);
    }

    /**
     * single client
     */
    if (isset($clients[$pk])) {
      $id = $clients[$pk];

      $data = $query->where("$apk = ?")->select($fields)->execute($id)->fetchAll();

      $clients[$name] = $data;
    }

    /**
     * multiple clients
     */
    else {
      $ids = array();
      foreach ($clients as $client) {
        $ids[] = $client[$pk];
      }

      if (! empty($ids)) {
        $fields[] = "$atable.$apk AS $apk";

        $ids = implode(', ', array_map(array($dao, 'quote'), $ids));

        $data = $query->select($fields)->where("$apk IN ($ids)")->execute()->fetchAll();

        foreach ($clients as &$client) {
          foreach ($data as $row) {
            if ($client[$pk] == $row[$apk]) {
              unset($row[$apk]);
              $client[$name][] = $row;
            }
          }

          if (! isset($client[$name]) && $def->isRequired()) {
            $client[$name] = array();
          }
        }
      }
    }
  }

}

?>